//
// Created by root on 4/29/17.
//

#include "../runner_scheme.h"

#define MAKE_INTEGER(_x)       make_integer(global_scheme, _x)
#define MAKE_FRACTION(_x,_y)   make_fraction(global_scheme, make_integer(global_scheme, _x), make_integer(global_scheme, _y))
#define MAKE_DECIMAL(_x)       make_decimal(global_scheme, _x)
#define MAKE_COMPLEX(_x,_y)    make_complex(global_scheme, _x, _y)

static void test_op_number_add_case1() {
    Cell* x1 = MAKE_INTEGER(1);
    Cell* x2 = MAKE_FRACTION(1, 2);
    Cell* x3 = MAKE_DECIMAL(0.5);
    Cell* x4 = MAKE_COMPLEX(MAKE_INTEGER(5), MAKE_INTEGER(6));
    Cell* ret = MAKE_COMPLEX(MAKE_DECIMAL(7.0),MAKE_INTEGER(6));
    Cell* args = cons(global_scheme, x4, &g_nil);
    args = cons(global_scheme, x3, args);
    args = cons(global_scheme, x2, args);
    args = cons(global_scheme, x1, args);
    global_scheme->args = args;
    op_add(global_scheme);
    Cell* r = global_scheme->returnValue;
    assert_true(eqn(r, ret));
}

static void test_op_number_add_case2() {
    Cell* x1 = MAKE_COMPLEX(MAKE_INTEGER(5), MAKE_INTEGER(6));
    Cell* x2 = MAKE_DECIMAL(0.5);
    Cell* x3 = MAKE_FRACTION(1, 2);
    Cell* x4 = MAKE_INTEGER(1);
    Cell* ret = MAKE_COMPLEX(MAKE_DECIMAL(7.0),MAKE_INTEGER(6));
    Cell* args = cons(global_scheme, x4, &g_nil);
    args = cons(global_scheme, x3, args);
    args = cons(global_scheme, x2, args);
    args = cons(global_scheme, x1, args);
    global_scheme->args = args;
    op_add(global_scheme);
    Cell* r = global_scheme->returnValue;
    assert_true(eqn(r, ret));
}

static void test_op_number_mul_case1() {
    Cell* x1 = MAKE_INTEGER(1);
    Cell* x2 = MAKE_FRACTION(1, 2);
    Cell* x3 = MAKE_DECIMAL(0.5);
    Cell* x4 = MAKE_COMPLEX(MAKE_INTEGER(5), MAKE_INTEGER(6));
    Cell* ret = MAKE_COMPLEX(MAKE_DECIMAL(1.25),MAKE_DECIMAL(1.5));
    Cell* args = cons(global_scheme, x4, &g_nil);
    args = cons(global_scheme, x3, args);
    args = cons(global_scheme, x2, args);
    args = cons(global_scheme, x1, args);
    global_scheme->args = args;
    op_mul(global_scheme);
    Cell* r = global_scheme->returnValue;
    assert_true(eqn(r, ret));
}

static void test_op_number_mul_case2() {
    Cell* x1 = MAKE_COMPLEX(MAKE_INTEGER(5), MAKE_INTEGER(6));
    Cell* x2 = MAKE_DECIMAL(0.5);
    Cell* x3 = MAKE_FRACTION(1, 2);
    Cell* x4 = MAKE_INTEGER(1);
    Cell* ret = MAKE_COMPLEX(MAKE_DECIMAL(1.25),MAKE_DECIMAL(1.5));
    Cell* args = cons(global_scheme, x4, &g_nil);
    args = cons(global_scheme, x3, args);
    args = cons(global_scheme, x2, args);
    args = cons(global_scheme, x1, args);
    global_scheme->args = args;
    op_mul(global_scheme);
    Cell* r = global_scheme->returnValue;
    assert_true(eqn(r, ret));
}

static void test_op_number_sub_case1() {
    Cell* x1 = MAKE_INTEGER(1);
    Cell* x2 = MAKE_FRACTION(1, 2);
    Cell* x3 = MAKE_DECIMAL(0.5);
    Cell* x4 = MAKE_COMPLEX(MAKE_INTEGER(5), MAKE_INTEGER(6));
    Cell* ret = MAKE_COMPLEX(MAKE_DECIMAL(-5), MAKE_INTEGER(-6));
    Cell* args = cons(global_scheme, x4, &g_nil);
    args = cons(global_scheme, x3, args);
    args = cons(global_scheme, x2, args);
    args = cons(global_scheme, x1, args);
    global_scheme->args = args;
    op_sub(global_scheme);
    Cell* r = global_scheme->returnValue;
    assert_true(eqn(r, ret));
}

static void test_op_number_sub_case2() {
    Cell* x1 = MAKE_COMPLEX(MAKE_INTEGER(5), MAKE_INTEGER(6));
    Cell* x2 = MAKE_DECIMAL(0.5);
    Cell* x3 = MAKE_FRACTION(1, 2);
    Cell* x4 = MAKE_INTEGER(1);
    Cell* ret = MAKE_COMPLEX(MAKE_DECIMAL(3), MAKE_INTEGER(6));
    Cell* args = cons(global_scheme, x4, &g_nil);
    args = cons(global_scheme, x3, args);
    args = cons(global_scheme, x2, args);
    args = cons(global_scheme, x1, args);
    global_scheme->args = args;
    op_sub(global_scheme);
    Cell* r = global_scheme->returnValue;
    assert_true(eqn(r, ret));
}

static void test_op_number_div_case1() {
    Cell* x1 = MAKE_INTEGER(1);
    Cell* x2 = MAKE_FRACTION(1, 2);
    Cell* x3 = MAKE_DECIMAL(0.5);
    Cell* x4 = MAKE_COMPLEX(MAKE_INTEGER(1), MAKE_INTEGER(2));
    Cell* ret = MAKE_COMPLEX(MAKE_DECIMAL(0.8), MAKE_DECIMAL(-1.6));
    Cell* args = cons(global_scheme, x4, &g_nil);
    args = cons(global_scheme, x3, args);
    args = cons(global_scheme, x2, args);
    args = cons(global_scheme, x1, args);
    global_scheme->args = args;
    op_div(global_scheme);
    Cell* r = global_scheme->returnValue;
    assert_true(eqn(r, ret));
}

static void test_op_number_div_case2() {
    Cell* x1 = MAKE_COMPLEX(MAKE_INTEGER(1), MAKE_INTEGER(2));
    Cell* x2 = MAKE_DECIMAL(0.5);
    Cell* x3 = MAKE_FRACTION(1, 2);
    Cell* x4 = MAKE_INTEGER(1);
    Cell* ret = MAKE_COMPLEX(MAKE_DECIMAL(4.0), MAKE_DECIMAL(8.0));
    Cell* args = cons(global_scheme, x4, &g_nil);
    args = cons(global_scheme, x3, args);
    args = cons(global_scheme, x2, args);
    args = cons(global_scheme, x1, args);
    global_scheme->args = args;
    op_div(global_scheme);
    Cell* r = global_scheme->returnValue;
    assert_true(eqn(r, ret));
}

static void test_number_to_string_integer() {
    Cell* x = MAKE_INTEGER(5);
    char* string = number_to_string(global_scheme, x);
    assert_string_equal(string, "5");
}

static void test_number_to_string_decimal() {
    Cell* x = MAKE_DECIMAL(3.14);
    char* string = number_to_string(global_scheme, x);
    assert_string_equal(string, "3.140000");
}

static void test_number_to_string_fraction() {
    Cell* x = MAKE_FRACTION(3, 4);
    char* string = number_to_string(global_scheme, x);
    assert_string_equal(string, "3/4");
}

static void test_number_to_string_complex() {
    Cell* x = MAKE_COMPLEX(MAKE_FRACTION(3, 4), MAKE_DECIMAL(3.14));
    char* string = number_to_string(global_scheme, x);
    assert_string_equal(string, "3/4+3.140000i");
}

//TODO
int main(int argc, char **argv) {
    const struct CMUnitTest tests[] = {
            /*add*/
            cmocka_unit_test(test_op_number_add_case1),
            cmocka_unit_test(test_op_number_add_case2),
            cmocka_unit_test(test_op_number_mul_case1),
            cmocka_unit_test(test_op_number_mul_case2),
            cmocka_unit_test(test_op_number_sub_case1),
            cmocka_unit_test(test_op_number_sub_case2),
            cmocka_unit_test(test_op_number_div_case1),
            cmocka_unit_test(test_op_number_div_case2),
            cmocka_unit_test(test_number_to_string_integer),
            cmocka_unit_test(test_number_to_string_decimal),
            cmocka_unit_test(test_number_to_string_fraction),
            cmocka_unit_test(test_number_to_string_complex),
    };
    return cmocka_run_group_tests(tests, setup_scheme, teardown_scheme);
}
